// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <zenhttp/httpserver.h>
#include <zenhttp/httpstatus.h>
#include <zenutil/zenserverprocess.h>

#include <memory>
#include <string_view>

ZEN_THIRD_PARTY_INCLUDES_START
#include <asio.hpp>
ZEN_THIRD_PARTY_INCLUDES_END

//////////////////////////////////////////////////////////////////////////
// Services
//

#include <zenhttp/auth/authmgr.h>
#include <zenhttp/auth/authservice.h>
#include <zenhttp/httpstats.h>
#include <zenhttp/httpstatus.h>
#include <zenhttp/httptest.h>
#include <zenstore/cache/structuredcachestore.h>
#include <zenstore/gc.h>
#include "admin/admin.h"
#include "cache/httpstructuredcache.h"
#include "diag/diagsvcs.h"
#include "frontend/frontend.h"
#include "objectstore/objectstore.h"
#include "projectstore/httpprojectstore.h"
#include "projectstore/projectstore.h"
#include "stats/statsreporter.h"
#include "upstream/upstream.h"
#include "vfs/vfsservice.h"
#include "workspaces/httpworkspaces.h"

#ifndef ZEN_APP_NAME
#	define ZEN_APP_NAME "Unreal Zen Storage Server"
#endif

namespace zen {

struct ZenServerOptions;

class ZenServer : public IHttpStatusProvider
{
	ZenServer& operator=(ZenServer&&) = delete;
	ZenServer(ZenServer&&)			  = delete;

public:
	ZenServer();
	~ZenServer();

	int	 Initialize(const ZenServerOptions& ServerOptions, ZenServerState::ZenServerEntry* ServerEntry);
	void InitializeState(const ZenServerOptions& ServerOptions);
	void InitializeStructuredCache(const ZenServerOptions& ServerOptions);

	void Run();
	void RequestExit(int ExitCode);
	void Cleanup();

	void SetDedicatedMode(bool State) { m_IsDedicatedMode = State; }
	void SetTestMode(bool State) { m_TestMode = State; }
	void SetDataRoot(std::filesystem::path Root) { m_DataRoot = Root; }
	void SetContentRoot(std::filesystem::path Root) { m_ContentRoot = Root; }

	std::function<void()> m_IsReadyFunc;
	void				  SetIsReadyFunc(std::function<void()>&& IsReadyFunc) { m_IsReadyFunc = std::move(IsReadyFunc); }
	void				  OnReady();

	void EnsureIoRunner();
	void EnqueueProcessMonitorTimer();
	void EnqueueStateMarkerTimer();
	void EnqueueSigIntTimer();
	void EnqueueStateExitFlagTimer();
	void EnqueueStatsReportingTimer();
	void CheckStateMarker();
	void CheckSigInt();
	void CheckStateExitFlag();
	void CheckOwnerPid();
	bool UpdateProcessMonitor();
	void ScrubStorage();
	void Flush();

	virtual void HandleStatusRequest(HttpServerRequest& Request) override;

private:
	ZenServerState::ZenServerEntry* m_ServerEntry	  = nullptr;
	bool							m_IsDedicatedMode = false;
	bool							m_TestMode		  = false;
	bool							m_IsPowerCycle	  = false;
	CbObject						m_RootManifest;
	std::filesystem::path			m_DataRoot;
	std::filesystem::path			m_ContentRoot;
	std::thread						m_IoRunner;
	asio::io_context				m_IoContext;
	asio::steady_timer				m_PidCheckTimer{m_IoContext};
	asio::steady_timer				m_StateMakerTimer{m_IoContext};
	asio::steady_timer				m_StateExitFlagTimer{m_IoContext};
	asio::steady_timer				m_SigIntTimer{m_IoContext};
	asio::steady_timer				m_StatsReportingTimer{m_IoContext};
	ProcessMonitor					m_ProcessMonitor;
	NamedMutex						m_ServerMutex;
	bool							m_FoundNoActiveSponsors = false;

	enum ServerState
	{
		kInitializing,
		kRunning,
		kShuttingDown
	} m_CurrentState = kInitializing;

	inline void				SetNewState(ServerState NewState) { m_CurrentState = NewState; }
	static std::string_view ToString(ServerState Value);

	StatsReporter					 m_StatsReporter;
	Ref<HttpServer>					 m_Http;
	std::unique_ptr<AuthMgr>		 m_AuthMgr;
	std::unique_ptr<HttpAuthService> m_AuthService;
	HttpStatusService				 m_StatusService;
	HttpStatsService				 m_StatsService;
	GcManager						 m_GcManager;
	GcScheduler						 m_GcScheduler{m_GcManager};
	std::unique_ptr<CidStore>		 m_CidStore;
	Ref<ZenCacheStore>				 m_CacheStore;
	HttpTestService					 m_TestService;
#if ZEN_WITH_TESTS
	HttpTestingService m_TestingService;
#endif
	RefPtr<ProjectStore>						m_ProjectStore;
	std::unique_ptr<HttpProjectService>			m_HttpProjectService;
	std::unique_ptr<Workspaces>					m_Workspaces;
	std::unique_ptr<HttpWorkspacesService>		m_HttpWorkspacesService;
	std::unique_ptr<UpstreamCache>				m_UpstreamCache;
	std::unique_ptr<HttpUpstreamService>		m_UpstreamService;
	std::unique_ptr<HttpStructuredCacheService> m_StructuredCacheService;
	HttpHealthService							m_HealthService;
	std::unique_ptr<HttpFrontendService>		m_FrontendService;
	std::unique_ptr<HttpObjectStoreService>		m_ObjStoreService;
	std::unique_ptr<VfsService>					m_VfsService;
	std::unique_ptr<JobQueue>					m_JobQueue;
	std::unique_ptr<HttpAdminService>			m_AdminService;

	bool m_DebugOptionForcedCrash = false;
	bool m_UseSentry			  = false;

	std::string m_StartupScrubOptions;
};

void zenserver_forcelinktests();

}  // namespace zen
